#include "GPU_Handler.h"

GPU_Handler::GPU_Handler(void)
{
	shaderProgram = NULL;
	shaderContext = NULL;
	shaderParameters.clear();
}

GPU_Handler::~GPU_Handler(void)
{
}

void GPU_Handler::SetShaderType( CGGLenum type )
{
	shaderType = type;	
}

bool GPU_Handler::Load( const char *filename, CGGLenum type )
{
	shaderParameters.clear();

	shaderName.clear();
	shaderName.append(filename);
	shaderType = type;
	shaderProfile = cgGLGetLatestProfile(type);

	if(shaderType == CG_GL_VERTEX){
		shaderProgram = cgCreateProgramFromFile(shaderContext, CG_SOURCE, shaderName.c_str(), shaderProfile, NULL, NULL);
	}else if(shaderType == CG_GL_FRAGMENT){
		shaderProgram = cgCreateProgramFromFile(shaderContext, CG_SOURCE, shaderName.c_str(), shaderProfile, NULL, NULL);
	}
	cgGetProgramString(shaderProgram, CG_COMPILED_PROGRAM);

	if(shaderProgram != NULL)
	{
		cgGLLoadProgram(shaderProgram);
		if(shaderProgram == NULL)
			return false;
		return true;
	}
	else
		return false;
}

void GPU_Handler::Unload()
{
	cgDestroyProgram(shaderProgram);
}

void GPU_Handler::AddParameter( char *name, CGGL_PARAMETER_TYPE type, void *ptr )
{
	if(shaderProgram == NULL) return;
	GPUParam newParam;

	if(ptr != NULL)
		newParam.ptrs = ptr;

	newParam.type = type;
	newParam.name.append(name);
	newParam.param = cgGetNamedParameter(shaderProgram, newParam.name.c_str());

	shaderParameters.push_back(newParam);
}

void GPU_Handler::RefreshParameter()
{
	int	tex_id = 1;
	int* id;
	float* value;
	for(int i=0; i<shaderParameters.size(); i++)
	{
		switch(shaderParameters[i].type)
		{
		case CG_GL_VECTOR_1D:
			cgGLSetParameter1fv(shaderParameters[i].param, (float *)shaderParameters[i].ptrs);
			value = (float*)shaderParameters[i].ptrs;
			break;
		case CG_GL_VECTOR_2D:
			cgGLSetParameter2fv(shaderParameters[i].param, (float *)shaderParameters[i].ptrs);
			break;
		case CG_GL_VECTOR_3D:
			cgGLSetParameter3fv(shaderParameters[i].param, (float *)shaderParameters[i].ptrs);
			break;
		case CG_GL_VECTOR_4D:
			cgGLSetParameter4fv(shaderParameters[i].param, (float *)shaderParameters[i].ptrs);
			value = (float*)shaderParameters[i].ptrs;
			break;
		case CG_GL_MODELVIEW_MTX:
			cgGLSetStateMatrixParameter(shaderParameters[i].param, CG_GL_MODELVIEW_MATRIX, CG_GL_MATRIX_IDENTITY);
			break;
		case CG_GL_MODELVIEW_PROJECTION_MTX:
			cgGLSetStateMatrixParameter(shaderParameters[i].param, CG_GL_MODELVIEW_PROJECTION_MATRIX, CG_GL_MATRIX_IDENTITY);
			break;
		case CG_GL_MODELVIEW_IT_MTX:
			cgGLSetStateMatrixParameter(shaderParameters[i].param, CG_GL_MODELVIEW_MATRIX, CG_GL_MATRIX_INVERSE_TRANSPOSE);
			break;
		case CG_GL_ARBITRARY_MTX:
			cgGLSetMatrixParameterfc(shaderParameters[i].param, (float *)shaderParameters[i].ptrs);
			value = (float *)shaderParameters[i].ptrs;
			break;
		case CG_GL_TEXTURE_2D:
		case CG_GL_TEXTURE_CUBE_MAP:
		case CG_GL_TEXTURE_RECT:
			id = (int*)shaderParameters[i].ptrs; 
			cgGLSetTextureParameter(shaderParameters[i].param, *id);
			cgGLEnableTextureParameter(shaderParameters[i].param);
			break;
		}
	}
}

void GPU_Handler::ShaderBind()
{
	if(this->shaderProgram)
	{
		cgGLBindProgram(this->shaderProgram);
		cgGLEnableProfile(this->shaderProfile);
		this->RefreshParameter();
	}
}

void GPU_Handler::ShaderRelease()
{
	if(this->shaderProgram)
		cgGLDisableProfile(this->shaderProfile);
}

void GPU_Handler::SetUpFBO( int width, int height, GLenum interFormat, GLenum format, GLuint type, GLenum target/*=GL_TEXTURE_RECTANGLE_ARB*/ )
{
	framebuffer.width=width;
	framebuffer.height=height;
	framebuffer.interFormat=interFormat;
	framebuffer.format=format;
	framebuffer.target=target;
	framebuffer.type=type;

	glewInit();

	glGenFramebuffersEXT(1,&(framebuffer.ID));
}

void GPU_Handler::InitTexBuffer( char *name, float *read, float *write/*=NULL*/ )
{
	glGenTextures(1,&(framebuffer.texList[READ]));
	glBindTexture(framebuffer.target,framebuffer.texList[READ]);
	glTexParameteri(framebuffer.target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(framebuffer.target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	glTexParameteri(framebuffer.target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	glTexParameteri(framebuffer.target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	glTexImage2D(framebuffer.target, 0, framebuffer.interFormat, framebuffer.width, framebuffer.height, 0, framebuffer.format, framebuffer.type, read);

	glGenTextures(1,&(framebuffer.texList[WRITE]));
	glBindTexture(framebuffer.target,framebuffer.texList[WRITE]);
	glTexParameteri(framebuffer.target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(framebuffer.target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	glTexParameteri(framebuffer.target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	glTexParameteri(framebuffer.target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	glTexImage2D(framebuffer.target, 0, framebuffer.interFormat, framebuffer.width, framebuffer.height, 0, framebuffer.format, framebuffer.type, write);

	AddParameter(name,CG_GL_TEXTURE_RECT,&(framebuffer.texList[READ]));
}

void GPU_Handler::FBOActivate()
{
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT,framebuffer.ID);

	glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT,GL_COLOR_ATTACHMENT0_EXT,framebuffer.target,framebuffer.texList[WRITE],NULL);	
}

void GPU_Handler::FBODeactivate()
{
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
}

void GPU_Handler::Swap()
{
	GLuint temp=framebuffer.texList[READ];
	framebuffer.texList[READ]=framebuffer.texList[WRITE];
	framebuffer.texList[WRITE]=temp;
}

void GPU_Handler::GPUCompute()
{
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluOrtho2D(0.0, framebuffer.width, 0.0, framebuffer.height);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	glViewport(0, 0, framebuffer.width, framebuffer.height);

	glPolygonMode(GL_FRONT,GL_FILL);

	glBegin(GL_QUADS);
	glTexCoord2f(0.0, 0.0); 
	glVertex2f(0.0, 0.0);
	glTexCoord2f(framebuffer.width, 0.0); 
	glVertex2f(framebuffer.width, 0.0);
	glTexCoord2f(framebuffer.width, framebuffer.height); 
	glVertex2f(framebuffer.width, framebuffer.height);
	glTexCoord2f(0.0, framebuffer.height); 
	glVertex2f(0.0, framebuffer.height);
	glEnd();
}
